Problema: Predicción de calorías quemadas (Clasificación)¶

Utilizando como referencia la tabla sobre actividad física y gasto calórico disponible en la página W3Schools.com, desarrolla un modelo de Machine Learning eficiente para la predicción, aplicando técnicas de análisis y selección de variables relevantes.

Paso 1: Recolección de datos¶

  • Revisar la tabla: Se examinan los datos proporcionados por la página w3schools.com para entender qué variables están disponibles.
  • Identificar la variable objetivo: En este caso, la variable que queremos predecir es el gasto calórico.
In [1]:
# Importar librerias
import pandas as pd
import plotly.io as pio

# Renderirzar graficos de plotly para verlos en el navegador web
pio.renderers.default = 'notebook'

# Leer datos descargados de W3School - Actividzd fisica y gasto calorico
df = pd.read_csv("../data/data_calorias.csv")
print(df)
     Duration  Pulse  Maxpulse  Calories
0          60    110       130     409.1
1          60    117       145     479.0
2          60    103       135     340.0
3          45    109       175     282.4
4          45    117       148     406.0
..        ...    ...       ...       ...
164        60    105       140     290.8
165        60    110       145     300.0
166        60    115       145     310.2
167        75    120       150     320.4
168        75    125       150     330.4

[169 rows x 4 columns]

Paso 2: Exploración de los datos¶

  • Verificar la estructura de la tabla: Usa DataFrame.info() para verificar el número de filas, columnas, tipos de datos y valores nulos.
  • Generar estadísticas: Usa DataFrame.describe() para obtener estadísticas descriptivas de las variables.
In [2]:
print("**! Estructura de la tabla !**")
print(df.info())

print("**! Estadisticas descriptivas !**")
print(df.describe())
**! Estructura de la tabla !**
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 169 entries, 0 to 168
Data columns (total 4 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   Duration  169 non-null    int64  
 1   Pulse     169 non-null    int64  
 2   Maxpulse  169 non-null    int64  
 3   Calories  164 non-null    float64
dtypes: float64(1), int64(3)
memory usage: 5.4 KB
None
**! Estadisticas descriptivas !**
         Duration       Pulse    Maxpulse     Calories
count  169.000000  169.000000  169.000000   164.000000
mean    63.846154  107.461538  134.047337   375.790244
std     42.299949   14.510259   16.450434   266.379919
min     15.000000   80.000000  100.000000    50.300000
25%     45.000000  100.000000  124.000000   250.925000
50%     60.000000  105.000000  131.000000   318.600000
75%     60.000000  111.000000  141.000000   387.600000
max    300.000000  159.000000  184.000000  1860.400000

Paso 3: Procesamiento de los datos¶

  • Limpiar de datos: Manejar valores faltantes (por ejemplo, llenar valores nulos con la media o eliminar filas/columnas), ademas eliminar o manejar datos duplicados.
  • Estandarizar varaibles: Transforma o normaliza los datos en caso de ser necesario.

La variable objetivo (y, en este caso Calories) contiene 5 valores nulos (NaN). Scikit-learn no puede procesar datos faltantes directamente, por lo que es necesario limpiar o manejar estos valores antes de entrenar el modelo.

In [3]:
# Verificar valores nulos
print(f"**! Varaibles con valores nulos: Dataframe original !**\n{df.isna().sum()}")

# Manejar valores nulos en la variable objetivo
df = df.dropna(subset=["Calories"])  # O imputar: df["Calories"].fillna(df["Calories"].mean(), inplace=True)

# Asegúrate de que no haya valores nulos
print(f"**! Varaibles con valores nulos: Dataframe depurado !**\n{df.isna().sum()}")
**! Varaibles con valores nulos: Dataframe original !**
Duration    0
Pulse       0
Maxpulse    0
Calories    5
dtype: int64
**! Varaibles con valores nulos: Dataframe depurado !**
Duration    0
Pulse       0
Maxpulse    0
Calories    0
dtype: int64

Nota: En un modelo de clasificación, es esencial que la variable objetivo sea categórica, ya que el propósito del modelo es asignar cada instancia (registro) a una categoría predefinida o etiqueta. Dado que estamos utilizando datos de un ejemplo originalmente diseñado para regresión, convertiremos las calorías en categorías para adaptar el problema y facilitar su ejemplificación en el contexto de clasificación.

In [4]:
# Calcular percentiles para establecer límites dinámicos y balancerar las categorias
# Funcion para categorización de Calories
def categorizar_calorias(Calories):
    if Calories < 200:
        return 'Baja'
    elif 200 <= Calories < 400:
        return 'Media'
    else:
        return 'Alta'

# Aplicar la función al dataset
df['Calories_Category'] = df['Calories'].apply(categorizar_calorias)
print(df)
     Duration  Pulse  Maxpulse  Calories Calories_Category
0          60    110       130     409.1              Alta
1          60    117       145     479.0              Alta
2          60    103       135     340.0             Media
3          45    109       175     282.4             Media
4          45    117       148     406.0              Alta
..        ...    ...       ...       ...               ...
164        60    105       140     290.8             Media
165        60    110       145     300.0             Media
166        60    115       145     310.2             Media
167        75    120       150     320.4             Media
168        75    125       150     330.4             Media

[164 rows x 5 columns]

Paso 4: Análisis de las variables¶

  • Explorar correlaciones: Calcula las correlaciones entre las variables independendientes con DataFrame.corr() para evaluar la colinealidad entre variables independientes. La alta colinealidad entre las variables puede afectar la estabilidad del modelo y su capacidad para funcionar correctamente.
  • Verificar categorias: Explora el balance entre las categorias de la variable objetivo. Un buen balance permite que el modelo aprenda de todas las categorias de manera eficiente.
  • Visualizar los datos: Utiliza gráficos como heatmap para representar gráficamente las relaciones entre variables independientes, histograma para ver el comportamieto de las categorias. y diagramas de dispersión para analizar relaciones clave.
In [5]:
# Correlación entre variables numéricas independientes
corr_matrix = df[['Duration', 'Pulse', 'Maxpulse']].corr()
print(corr_matrix)
          Duration     Pulse  Maxpulse
Duration  1.000000 -0.160661  0.005679
Pulse    -0.160661  1.000000  0.784631
Maxpulse  0.005679  0.784631  1.000000
In [6]:
import plotly.express as px

# Crear el heatmap de correlación
fig = px.imshow(corr_matrix, text_auto=True, color_continuous_scale='Blues', title="Heatmap de Correlación")
fig.show()

Pulse vs. Maxpulse: La correlación es 0.7846, lo que indica una relación fuerte entre Pulse y Maxpulse. Esto es un indicativo de que estas dos variables podrían estar altamente correlacionadas, lo que significa que podrían estar proporcionando información redundante al modelo. En este caso, eliminaremos una de estas variables (Pulse) del modelo para evitar problemas de colinealidad.

In [7]:
# Verificación de clases de la variable objetivo
df['Calories_Category'].value_counts()  
Out[7]:
Calories_Category
Media    106
Alta      37
Baja      21
Name: count, dtype: int64
In [8]:
import plotly.express as px

# Crear el histograma con Plotly
fig = px.histogram(df, x='Calories_Category', title='Distribución de las Categorías de Calorías')

# Mostrar el gráfico
fig.show()

La distribución de las clases muestra un notable desbalance: la categoría "Media" tiene significativamente más instancias (106) en comparación con "Alta" (37) y "Baja" (21). Es necesario analizar más profundamente el reporte de clasificación y la matriz de confusión para interpretar correctamente el desempeño en las clases minoritarias ("Alta" y "Baja").

In [9]:
import plotly.express as px

# Suponiendo que 'df' es tu dataframe y tienes las columnas 'Duracion', 'Maxpulse' y 'Calories_Category'
fig = px.scatter_matrix(df, 
                        dimensions=["Duration", "Pulse", "Maxpulse"], 
                        color="Calories_Category", 
                        title="Matriz de Dispersión: Duració y Máximo Pulso")
fig.show()

Existe una buena separacion entre las categorias (Baja, Media, Alta), esto sugiere que la clasificación basada en las variables (Duración y Pulso Maximo) podría funcionar bien. Si se solapan demasiado, podría indicar que las variables no son tan predictivas.

Paso 5: Selección de variables relevantes¶

  • Seleccionar variables: Basado en las correlaciones y visualizaciones, se seleccionan las variables predictoras más relevantes para explicar la quema de calorías.

Correlación entre variables predictoras:

  • Pulse y Maxpulse: Como Pulso y Máximo Pulso están fuertemente correlacionados (0.7846), sería conveniente eliminar una de estas dos (Pulso).

  • Duración: Dado que Duración tiene una correlación muy baja tanto con Pulso como con Máximo Pulso, puede seguir siendo una variable útil para el modelo de predicción de calorías.

Paso 6: Construcción del modelo¶

  • Dividir los datos: Los datos se dividen en conjuntos de entrenamiento y prueba, comunmente (80%-20%).
  • Seleccionar el modelo: Dado que las calorías se han convertido en una variable categórica con clases como "Baja", "Media" y "Alta", y el objetivo es predecir una categoría, la elección más adecuada para este análisis es utilizar un Modelo de Clasificación.
  • Entrenar el modelo: Permite ajustar el modelo con los datos de entrenamiento y las variables seleccionadas.
  • Evaluar el modelo: Calcular métricas de desempeño como:
    • Precisión (Accuracy): Mide la proporción de predicciones correctas sobre el total de predicciones. Es útil cuando las clases están balanceadas. En caso de desbalance, puede ser engañosa.
    • Recall o Sensibilidad: Mide la proporción de verdaderos positivos correctamente identificados frente al total de verdaderos positivos reales.
    • F1-Score: Es la media armónica de precisión y recall, que equilibra ambas métricas. Es útil cuando las clases están desbalanceadas y necesitas un equilibrio entre precisión y recall
    • Matriz de Confusión: Es una tabla que muestra las predicciones correctas e incorrectas clasificadas por clase.
  • Predecir valores: Mostramos cómo predecir calorías para una nueva entrada.

Utilizaremos un Modelo de Regresión Logística Multiclase, dado que tienes tres categorías para las calorías, la regresión logística multiclase es un modelo adecuado. Este modelo puede predecir la probabilidad de que una observación pertenezca a cada una de las tres clases, y luego asignar la clase con la mayor probabilidad.

Modelo de Regresion Logística

In [10]:
# importar librerias
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix

# Codificar las categorías con números (esto es necesario para los modelos de clasificación)
le = LabelEncoder()
df['Calories_Category'] = le.fit_transform(df['Calories_Category'])

# Seleccionar las variables predictoras y la variable objetivo (categoría)
X = df[["Duration", "Maxpulse"]]  # Variables predictoras
y = df["Calories_Category"]  # Variable objetivo transformada a categorías

# Dividir los datos en conjuntos de entrenamiento y prueba
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Crear y entrenar el modelo de clasificación (Regresión Logística)
model = LogisticRegression()
model.fit(X_train, y_train)

# Realizar predicciones
y_pred = model.predict(X_test)

# Evaluar el modelo
print("Exactitud del modelo:", accuracy_score(y_test, y_pred))
print("\nReporte de clasificación:\n", classification_report(y_test, y_pred))
print("\nMatriz de confusión:\n", confusion_matrix(y_test, y_pred))

# Ejemplo de predicción
new_data = pd.DataFrame({"Duration": [70], "Maxpulse": [140]})
predicted_category = model.predict(new_data)
predicted_category_label = le.inverse_transform(predicted_category)
print("\nCategoría predicha para las calorías:", predicted_category_label)
Exactitud del modelo: 0.8484848484848485

Reporte de clasificación:
               precision    recall  f1-score   support

           0       1.00      0.67      0.80         6
           1       0.67      0.80      0.73         5
           2       0.87      0.91      0.89        22

    accuracy                           0.85        33
   macro avg       0.85      0.79      0.81        33
weighted avg       0.86      0.85      0.85        33


Matriz de confusión:
 [[ 4  0  2]
 [ 0  4  1]
 [ 0  2 20]]

Categoría predicha para las calorías: ['Media']

Interpretación del modelo

  • Exactitud general del modelo (Accuracy): El valor obtendio de 0.8484 indica que el modelo clasifica correctamente el 85% de las instancias en el conjunto de datos. Este es un buen desempeño general.
  • Matriz de confusión:
    • El modelo clasifica correctamente 20 de las 22 instancias reales de "Alta," 4 de las 6 de "Baja," y 4 de las 5 de "Media."
    • Hay confusión entre "Baja" y "Alta" (2 instancias clasificadas erróneamente) y entre "Media" y "Alta" (1 instancia).
  • Calorías predichas: La predicción de gasto calorico Medio parece ser razonable dependiendo de los valores de Duration (70) y Maxpulse (140).

Paso 7: Conclusión sobre la Confiabilidad del Modelo: Regresión Logística¶

El modelo tiene un buen desempeño global, pero:

  1. La clase "Alta" está mejor representada debido a su mayor frecuencia en los datos.
  2. Las clases "Baja" y "Media" presentan menor recall, lo que indica que el modelo tiene más dificultades para identificar correctamente estas clases.

Exportar a HTML

jupyter nbconvert --to html notebook/regresion_logistica.ipynb